﻿using System;
using System.Collections.Generic;
using System.Text;
using Tao.DevIl;
using System.IO;
using Tao.OpenGl;
namespace AADD
{
    public class Spritesheet {
        private string fileName;    // name of the picture file that this has loaded
        private int glTexID;        // GL texture ID used to bind the texture
        private int width;          // width of image in pixels
        private int height;         // height of image in pixels
        private int rows;           // #rows in the tilesheet, computed in constructor
        private int cols;           // #cols in the tilesheet, computed in constructor
        private int depth; // depth (unused)
        private int bpp;   // bits per pixel (unused)
        public int highestTileIndex;

        public float texturePixelOffsetX;   // Each time we blit one tile, we need texture coordinates (0.0 -> 1.0). Since each tile is main.TILESIZE,
                                            // we can do a calculation once to figure out where the texture coordinates end.
        public float texturePixelOffsetY;

        #region Properties
        public int GlTexID
        {
            get { return glTexID; }
        }

        public int Width
        {
            get { return width; }
        }

        public int Height
        {
            get { return height; }
        }

        public int Rows
        {
            get { return rows; }
        }

        public int Cols
        {
            get { return cols; }
        }
        #endregion

        public Spritesheet(string fileName)
        {
            this.fileName = fileName;
            string fullPath = main.RESOURCEDIRECTORY + fileName;

            if (!File.Exists(fullPath))
            {
                throw new Exception(string.Format("{0} doesn't exist. Error coming from Spritesheet constructor.", fullPath));
            }

            // If the following line gives a corrupted memory error, then Il.IlInit() was not called.
            // The spritesheet manager should not have to do this!
            
            //glTexID = Ilut.ilutGLLoadImage(fullPath);
            int ilTexID = Il.ilGenImage();
            Il.ilBindImage(ilTexID);


            if (!Il.ilLoadImage(fullPath) || !Il.ilConvertImage(Il.IL_RGBA, Il.IL_UNSIGNED_BYTE))
            {
                throw new Exception(string.Format("Some problem loading or converting the image: {0}", fullPath));
            }

            width = Il.ilGetInteger(Il.IL_IMAGE_WIDTH);
            height = Il.ilGetInteger(Il.IL_IMAGE_HEIGHT);
            depth = Il.ilGetInteger(Il.IL_IMAGE_DEPTH);
            bpp = Il.ilGetInteger(Il.IL_IMAGE_BITS_PER_PIXEL);

            Gl.glGenTextures(1, out glTexID);
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, glTexID);


            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST);
            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST);
            Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA8, width, height, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, Il.ilGetData());

            rows = height / main.TILESIZE;
            cols = width / main.TILESIZE;
            highestTileIndex = rows * cols;

            texturePixelOffsetX = (float)main.TILESIZE / width;
            texturePixelOffsetY = (float)main.TILESIZE / height;

            if (((((float)height) / main.TILESIZE) != rows) || ((((float)width) / main.TILESIZE) != cols ) ) {
                Console.WriteLine( "Warning! Either the height or the width of the image is not a multiple of the tilesize!" +
                    "\nHeight: {0}  Width: {1}  Tilesize: {2}", height, width, main.TILESIZE );
            }

            Console.WriteLine("Loaded image. Filename= {0}  width = {1}   height = {2}", fileName, width, height);

        }
    }

    public class SpritesheetManager
    {
        int whatTextureIsBound;
        Dictionary<string, Spritesheet> spritesheets; // key = fileName, value = Spritesheet associated with it

        public SpritesheetManager()
        {
            spritesheets = new Dictionary<string,Spritesheet>();
            whatTextureIsBound = -1; // OpenGL cannot assign to texID -1, so this means nothing is bound
        }
        
        private void createSpritesheet( string fileName ) {

            // If we already made the sprite sheet before, then do nothing
            if (spritesheets.ContainsKey(fileName))
            {
                return;
            }

            Spritesheet newSheet = new Spritesheet(fileName);
            spritesheets.Add(fileName, newSheet);
        }

        public void blit(string fileName, int tileIndex)
        {
            if (!spritesheets.ContainsKey(fileName))
            {
                createSpritesheet(fileName);
            }

            // At this point, spritesheets is guaranteed to contain the spritesheet we need

            Spritesheet sheetToUse = spritesheets[fileName];

            // If the sheet we want to use isn't bound, then bind it
            if (whatTextureIsBound != sheetToUse.GlTexID)
            {
                whatTextureIsBound = sheetToUse.GlTexID;
                Gl.glBindTexture(Gl.GL_TEXTURE_2D, sheetToUse.GlTexID);
            }

            // At this point, the sheet we want to use is bound and we can just blit from it
            //
            // First, figure out where on the image we're going to blit from
            int imageX = (tileIndex % sheetToUse.Cols) * main.TILESIZE;
            int imageY = (tileIndex / sheetToUse.Cols) * main.TILESIZE;

            // Next, figure out what the texture coordinates are.
            float texX = (float)imageX / sheetToUse.Width;
            float texY = (float)imageY / sheetToUse.Height;

            if (tileIndex < 0 || tileIndex >= sheetToUse.highestTileIndex)
            {
                Console.WriteLine("Trying to access tile index: {0} but the max is {1}", tileIndex, sheetToUse.highestTileIndex);
                Console.WriteLine("(allowing the call anyway because it should work)");
            }
            
            Gl.glEnable(Gl.GL_TEXTURE_2D);

            Gl.glBegin(Gl.GL_QUADS);
            {
                // We did 1.0f-texY because our origin is at the upper left and the origin of the image
                // is at the bottom left, so texMaxHeight (1.0f) - coordinate
                Gl.glTexCoord2f(texX, 1.0f - texY);
                Gl.glVertex2i(0, 0);

                Gl.glTexCoord2f(texX + sheetToUse.texturePixelOffsetX, 1.0f - texY);
                Gl.glVertex2i(main.TILESIZE, 0);

                Gl.glTexCoord2f(texX + sheetToUse.texturePixelOffsetX, 1.0f - (texY + sheetToUse.texturePixelOffsetY));
                Gl.glVertex2i(main.TILESIZE, main.TILESIZE);

                Gl.glTexCoord2f(texX, 1.0f - (texY + sheetToUse.texturePixelOffsetY));
                Gl.glVertex2i(0, main.TILESIZE);
            }
            Gl.glEnd();
        }

    }
}
